home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / CUGUK / COMMS / C100.ZIP / KERMIT.ZIP / CKXKER.BLD < prev    next >
Text File  |  1990-01-31  |  51KB  |  1,513 lines

  1. Date:     23 May 1988
  2. From:     Adrian Godwin, Thorn EMI Micrologic, Bedford, England.
  3. Subject:  C-Kermit 4E(070) Implementation Notes for Minix
  4.  
  5.     These notes discuss changes I made to the 4E(070) C-Kermit to run
  6. it efficiently under Minix 1.1. The earlier 4D(061) version was used 
  7. originally - the changes to kermit have been slightly simplified in this
  8. version. Kermit source code changes (principally in ckufio and ckutio) 
  9. are attached. The changes are sufficiently minor that they may be supported
  10. through conditional compilation of the standard Unix file set, but the 
  11. modified files have all been given names of the form ckx..., pending full
  12. testing of the modifications under other systems.
  13.  
  14.     Hints, fixes and library changes are also attached - most of 
  15. these are applicable for anyone implementing a serial i/o driver for Minix, 
  16. and many library fixes are useful for porting other utilities.
  17.  
  18.     Kermit cannot be built under version 1.1 Minix, as it compiles to
  19. about 85K and the initial Minix assembler cannot produce separate I&D model 
  20. output. The executable file ckxker.out was therefore built under MS-DOS 
  21. using the Lattice 3.10 C compiler. Some care is needed in cross-compiling : 
  22. see the notes about library building in Tanenbaum's book, and read the 
  23. Lattice makefile, ckxker.mak. The library functions setjmp() and longjmp(),
  24. in particular, will need recoding to suit the compiler you use.
  25.  
  26.  
  27. Associated Files:
  28. =================
  29.  
  30.     I have included with this build file (ckxker.bld) the following :
  31.  
  32. ckxtio.c    -    Modified Kermit source files    
  33. ckxfio.c    
  34.  
  35. ckxker.mak    -    Makefile for Lattice 'lmk' and 3.10 compiler.
  36.  
  37. ckxker.inp    -    linker command file (included in ckxker.mak)
  38.  
  39. ckxker.out    -    Minix executable for use if you can't build
  40.             Kermit yourself under MS-DOS.
  41.  
  42. ckxker.boo    -    ascii - encoded executable file for ease of
  43.             transfer.
  44.  
  45.  
  46.  
  47. The following filter (based on the ms-kermit example) may be used to 
  48. 'deboo' ckxker.boo into a Minix executable. The filename embedded in the
  49. .boo file is ignored - direct standard output to 'kermit'.
  50.  
  51.  
  52. #include <stdio.h>
  53.  
  54. #define fixchar(x)    ((x)-'0')
  55. #define NULLCHR        fixchar('~')
  56.  
  57. main()
  58. {
  59.     int a,b,c,d,repeat;
  60.  
  61.     /* skip the line with the filename */
  62.  
  63.     while(getchar() != '\n')
  64.         ;
  65.  
  66.     while( (c = getchar()) != EOF) {
  67.  
  68.         if (c == '\n')
  69.             continue;
  70.  
  71.         if (fixchar(c) == NULLCHR) {
  72.             repeat = fixchar(getchar());
  73.             while (repeat-- > 0)
  74.                 putchar(0);
  75.         }
  76.         else {
  77.  
  78.             a = fixchar(c);
  79.             b = fixchar(getchar());
  80.             c = fixchar(getchar());
  81.             d = fixchar(getchar());
  82.  
  83.             putchar( ((a*4)  + (b/16)) & 0xff);
  84.             putchar( ((b*16) + (c/4))  & 0xff);
  85.             putchar( ((c*64) + d) & 0xff);
  86.         }
  87.     }
  88.     _cleanup();
  89. }
  90.  
  91.  
  92.  
  93.  
  94. Testing
  95. =======
  96.  
  97.     The executable file has been tested against itself (a second kermit
  98. process on the same machine), against Kermit-32 3.3.111 on VAX/VMS, and 
  99. against MS-Kermit 2.30 on a PC-AT. All successfully transferred files and
  100. performed host commands, such as 'help' and 'dir'. The version has not
  101. been tested exhaustively - in particular the parity and flowcontrol options
  102. for IBM, and the dialling and script commands, have not been tested at all.
  103.  
  104.     Performance was not particularly good  -  22% to 30% on receive
  105. (depending on whether a Minix - or Lattice -  compiled kernel was used) and 
  106. up to 39% on send - but this is a general problem with C-Kermit. The PS/2
  107. mentioned above managed only 31% efficiency on receiving from the same 
  108. lightly - loaded VAX.
  109.  
  110.     All testing was performed at 9600 baud. Testing at 4800 baud gave
  111. a significant reduction in errors, but not enough to improve on the 9600
  112. baud throughput. A worthwhile improvement was obtained by reducing timeouts
  113. to 1 to 2 seconds.
  114.  
  115.  
  116.     The integration of Minix conditional compilation options has been
  117. tested by successfully making 'SCO286' on a PS/2 model 60 and 'SYS3' 
  118. on a Convergent Technologies Mini-20. I have not been able to test a
  119. 'make V7', where bugs are most likely to have been introduced.
  120.  
  121.  
  122.  
  123. Changes to Kermit:
  124. ==================
  125.  
  126. ckutio.c    Most of the changes are in this module. Although Minix is
  127.         closely based on V7 UNIX, the V7 conditionals in this module
  128.         are almost exclusively to enable the use of kernel buffer
  129.         peeking for typahead determination. Minix does not have a V7
  130.         kernel, so this won't work. Minix does, however, permit non-
  131.         blocking reads. The V7 define used elsewhere for MINIX 
  132.         compatibility is #undefined here - a new symbol, V7_MINIX 
  133.         is defined for selecting the code that is common to both.
  134.  
  135.         Kermit will work perfectly well using MYREAD, but for
  136.         trapping console aborts during file transfer, the current
  137.         implementation needs to know if there are any characters in
  138.         the kernel buffers. It is worth implementing a FIONREAD
  139.         ioctl call to permit this.
  140.  
  141.         To save disc space, I've dummied out the uucp tty locking
  142.         code for the Minix version. It can easily be restored if 
  143.         required.
  144.  
  145.         For some reason, Lattice returns the unsigned char result
  146.         of myread() expanded to a signed int. A badly read character
  147.         can thus appear to other functions as an error code. Make
  148.         myread return ((int)ch) & 0377.
  149.  
  150.  
  151. ckufio.c    No include file <sys/files.h>. Lattice would prefer the 
  152.         functions FILE *fopen(), *fdopen() to be declared as such.
  153.         The function zclosif() attempts to kill a completed child
  154.         process. If it has already completed, this fails, but MINIX
  155.         still wants a wait() call to clean up the memory allocation,
  156.         otherwise a few invocations of 'remote directory' will use
  157.         up any remaining memory. This problem will also occur in the
  158.         minix shell if you invoke all processes in the background.
  159.         Use the shell command 'wait' to clean up completed processes
  160.         (zombies).
  161.  
  162.  
  163.  
  164.  
  165. Changes to Minix library routines.
  166. ==================================
  167.  
  168.     I started developing this set of library fixes when building Micro-
  169. Emacs. A few of them may be unnecessary, but I recommend you fix them anyway.
  170. Many routines are here because they aren't in the standard library.
  171. The lattice makefile includes all these routines as an object called 
  172. 'emlib' - this is for debugging purposes only (they should be built into 
  173. the library). While not strictly a library change, fix dos2out before 
  174. attempting to convert this large executable : the calculation of load_size 
  175. will be wrong unless all the operands are forced to type long. The 
  176. calculation of the total memory allocation, a_totb is also wrong, since it 
  177. includes (for the separate I & D model) the text size a_text in the sum. 
  178. This results in a 154K memory allocation, which is not permitted. 
  179.     chmem =20000 
  180. will fix it. The .exe header generated by the Microsoft linker places 0 in 
  181. the 'bytes in last page' slot for a load module which exactly fits in an 
  182. integral number of sectors. This is misunderstood by dos2out in two places :
  183. it makes an entry in the header for a data segment size 512 bytes too small,
  184. and copies 512 bytes too few to the output file. This may cause hard-to-find
  185. bugs that appear and disappear on relinks.
  186.  
  187.     Some include file changes. Stdio.h defines a macro 'puts()' as an 
  188. fputs() to stdout. This is wrong, as puts() should append a newline while 
  189. fputs() doesn't. Remove the macro and create a library function.
  190.  
  191. There is no <sys/dir.h>. It should contain:
  192.  
  193. /* dir.h
  194.  *
  195.  * The structure of a directory entry.
  196.  */
  197.  
  198. #define NAME_SIZE 14        /* defined in fs/const.h */
  199.  
  200. struct direct {
  201.   inode_nr d_ino;        /* inode number */
  202.   char d_name[NAME_SIZE];    /* name fills it out to 16 bytes */
  203. } ;
  204.  
  205.     The file h/type.h is needed by kermit, but included as <sys/types.h>. 
  206. Create a <sys/types.h> that includes h/type.h.
  207.  
  208.     Sgtty.h also needs some changes to define tty functions that don't 
  209. exist in standard Minix. The kermit I have built used this version of sgtty.h, 
  210. so if you want to use that executable, your tty driver must correspond. The 
  211. i/o functions are an upwards compatible superset of the Minix sgtty functions,
  212. and are inspired by SCO Xenix. Note particularly the implementation of
  213. stty() and gtty() as macros. 
  214.  
  215. /* sgtty.h 
  216.  *
  217.  * Data structures for IOCTL. 
  218.  */
  219.  
  220. struct sgttyb {
  221.   char sg_ispeed;        /* input speed      (has precedence)    */
  222.   char sg_ospeed;        /* output speed     (may be ignored)    */
  223.   char sg_erase;        /* erase character             */
  224.   char sg_kill;            /* kill character             */
  225.   int  sg_flags;        /* mode flags                 */
  226. };
  227.  
  228. /* simulate stty and gtty with ioctl                     */
  229.  
  230. #define stty(fd, arg)    ioctl(fd, TIOCSETP, arg)
  231. #define gtty(fd, arg)    ioctl(fd, TIOCGETP, arg)
  232.  
  233.  
  234. struct tchars {
  235.   char t_intrc;            /* SIGINT char                 */
  236.   char t_quitc;            /* SIGQUIT char             */
  237.   char t_startc;        /* start output (initially CTRL-Q)     */
  238.   char t_stopc;            /* stop output    (initially CTRL-S)     */
  239.   char t_eofc;            /* EOF (initially CTRL-D)         */
  240.   char t_brkc;            /* input delimiter (like nl)         */
  241. };
  242.  
  243. /* Fields in sg_flags. */
  244.  
  245. #define COOKED       0000000    /* neither CBREAK nor RAW         */
  246. #define    TANDEM         0000001    /* XON XOFF flowcontrol on input    */
  247. #define CBREAK         0000002    /* enable cbreak mode             */
  248. #define LCASE        0000004    /* support lower case            */
  249. #define ECHO         0000010    /* echo input                 */
  250. #define CRMOD         0000020    /* map lf to cr + lf             */
  251. #define RAW         0000040    /* enable raw mode             */
  252. #define ODDP         0000100    /* odd parity                */
  253. #define EVENP        0000200    /* even parity                */
  254. #define ANYP         0000300    /* parity not set / ignored        */
  255. #define XTABS         0006000    /* do tab expansion             */
  256.  
  257.  
  258. #define TIOCGETP (('t'<<8) | 8)
  259. #define TIOCSETP (('t'<<8) | 9)
  260. #define TIOCGETC (('t'<<8) | 18)
  261. #define TIOCSETC (('t'<<8) | 17)
  262.  
  263. /*
  264.  * I can't find a 'standard' definition for this one, so I've assigned
  265.  * a '1' arbitrarily.
  266.  */
  267.  
  268. #define FIONREAD (('f'<<8) | 1)
  269.  
  270. /* Baud rate settings for async ttys. */
  271.  
  272. #define B0        0        /* hangup line (drop DTR)    */
  273. #define B50        1
  274. #define B75        2
  275. #define B110        3
  276. #define B134        4
  277. #define B150        5
  278. #define B200        6
  279. #define B300        7
  280. #define B600        8
  281. #define B1200        9
  282. #define B1800        10
  283. #define B2400        11
  284. #define B4800        12
  285. #define B9600        13
  286.  
  287. #define EXTA        14
  288. #define B19200        EXTA
  289.  
  290. #define EXTB        15
  291. #define B38400        EXTB
  292.  
  293.  
  294.     There are no time management calls in MINIX other than time, times()
  295. etc. I have defined asctime() and gmtime() functions based on the conversions
  296. done in date.c, but avoided support for timezones and daylight saving.
  297.  
  298.  
  299. /* time.h : structure and function definitions for time library calls. */
  300.  
  301. struct tm {
  302.     int    tm_sec;
  303.     int    tm_min;
  304.     int    tm_hour;
  305.     int    tm_mday;
  306.     int    tm_mon;
  307.     int    tm_year;
  308.     int    tm_wday;
  309.     int    tm_yday;
  310.     int    tm_isdst;    /* not currently supported */
  311. };
  312.  
  313. extern struct tm    *localtime();
  314. extern struct tm     *gmtime();
  315. extern char        *asctime();
  316.  
  317.  
  318. /* system(command)
  319.  * 
  320.  * Code copied from the MINIX make utility 'mysystem' function, with
  321.  * minor modifications.
  322.  */
  323.  
  324. char *_defpath = "/bin/sh";
  325.  
  326. system(cmd)
  327. char *cmd;
  328. {
  329.     int ccode,pid,status;
  330.     char *shell, *getenv();
  331.  
  332.     if ( (shell = getenv("SHELL")) == NULL)
  333.     shell = _defpath;
  334.  
  335.     if ( (pid = fork()) == 0 ) {    /* child execs a shell */
  336.     execl(shell,shell,(*cmd ? "-c" : "-i"),cmd,0);
  337.     }
  338.  
  339.     if ( pid < 0 ) {              /* parent waits for child */
  340.     return(pid);
  341.     }
  342.     else {
  343.     while ( ((ccode = wait(&status)) != pid) && (ccode != -1))
  344.         ;
  345.     return(status);
  346.     }
  347. }
  348.  
  349.  
  350.  
  351. /* This function copied from the MINIX library, but with some bugs fixed. */
  352.  
  353. char *getenv(name)
  354. register char *name;
  355. {
  356.   extern char **environ;
  357.   register char **v = environ, *p, *q;
  358.  
  359.   while ((p = *v++) != NULL) {        /* fix : increment v */
  360.     q = name;
  361.     while (*p++ == *q)
  362.         if (*q++ == 0)
  363.             break;    /* fix : break rather than continue */
  364.     if (*(p - 1) != '=')
  365.         continue;
  366.     return(p);
  367.   }
  368.   return(0);
  369. }
  370.  
  371.  
  372.  
  373. /* getc
  374.  *
  375.  * fixed to ensure _count always indicates number of chars in the buffer.
  376.  * - used to indicate correctly only when zero. Decrement _count after
  377.  * taking char from recently filled buffer. Note : fseek() had a bodge 
  378.  * which allowed it to work with this error. This also requires a fix.
  379.  *
  380.  * note when rebuilding library : this module should appear AFTER scanf,
  381.  * which calls it, to ensure it can be found.
  382.  */
  383.  
  384. #include "stdio.h"
  385.  
  386. getc(iop)
  387. FILE *iop;
  388. {
  389.     int ch;
  390.  
  391.     if ( testflag(iop, (_EOF | _ERR )))
  392.         return (EOF); 
  393.  
  394.     if ( !testflag(iop, READMODE) ) 
  395.         return (EOF);
  396.  
  397.     if (--iop->_count < 0){        /* changed from <= 0 */
  398.  
  399.         if ( testflag(iop, UNBUFF) )
  400.             iop->_count = read(iop->_fd,&ch,1);
  401.         else 
  402.             iop->_count = read(iop->_fd,iop->_buf,BUFSIZ);
  403.  
  404.         if (iop->_count <= 0){
  405.             if (iop->_count == 0)
  406.                 iop->_flags |= _EOF;
  407.             else 
  408.                 iop->_flags |= _ERR;
  409.  
  410.             return (EOF);
  411.         }
  412.         else {
  413.             iop->_ptr = iop->_buf;
  414.             iop->_count--;        /* inserted */
  415.         }
  416.     }
  417.  
  418.     if (testflag(iop,UNBUFF))
  419.         return (ch & CMASK);
  420.     else
  421.         return (*iop->_ptr++ & CMASK);
  422. }
  423.  
  424. /* ttyname() - missing library function. Gets the inode number of the 
  425.  * passed filedescriptor, then searches the /dev directory for a match.
  426.  * Returns a pointer to the name (static data) or NULL.
  427.  */
  428.  
  429. #include "stdio.h"
  430. #include "stat.h"
  431. #include "dir.h"
  432.  
  433. char *ttyname(fd)
  434. int fd;
  435. {
  436.   struct stat s;
  437.   int n, dd;
  438.   char *p = NULL;
  439.   static struct direct d[2];    /* big enough to nul-terminate name */
  440.  
  441.   if (fstat(fd, &s) != 0 || (s.st_mode&S_IFMT) != S_IFCHR)
  442.     return(NULL);
  443.  
  444.   /* isatty. now search /dev for the inode. */
  445.  
  446.     n = s.st_ino;
  447.     if ((dd = open("/dev", 0)) < 0)
  448.         return NULL;        /* cannot read directory */
  449.  
  450.     while (read( dd, d, sizeof(*d)) == sizeof(*d)) 
  451.  
  452.         if (d[0].d_ino == n) {
  453.             p = d[0].d_name;
  454.             p[sizeof(*d)] = '\0';
  455.             break;
  456.         }
  457.     close(dd);
  458.     return(p);
  459. }
  460.  
  461. /* fdopen() - missing library function. Returns a file pointer set up from
  462.  * the passed file descriptor, or NULL if it can't.
  463.  */
  464.  
  465. #include "stdio.h"
  466.  
  467. FILE *fdopen(fd,mode)
  468. int fd;
  469. char *mode;
  470. {
  471.     register int i;
  472.     FILE *fp;
  473.     char *malloc();
  474.     int flags = 0;
  475.  
  476.     for (i = 0; _io_table[i] != 0 ; i++) 
  477.         if ( i >= NFILES )
  478.             return(NULL);
  479.  
  480.     switch(*mode){
  481.  
  482.     case 'w':
  483.     case 'a': 
  484.         flags |= WRITEMODE;
  485.         break;
  486.  
  487.     case 'r':
  488.         flags |= READMODE;    
  489.         break;
  490.  
  491.     default:
  492.         return(NULL);
  493.     }
  494.  
  495.  
  496.     if (( fp = (FILE *) malloc (sizeof( FILE))) == NULL )
  497.         return(NULL);
  498.  
  499.     fp->_count = 0;
  500.     fp->_fd = fd;
  501.     fp->_flags = flags;
  502.     fp->_buf = malloc( BUFSIZ );
  503.     if ( fp->_buf == NULL )
  504.         fp->_flags |=  UNBUFF;
  505.     else 
  506.         fp->_flags |= IOMYBUF;
  507.  
  508.     fp->_ptr = fp->_buf;
  509.     _io_table[i] = fp;
  510.     return(fp);
  511. }
  512.  
  513. /* modified exit() which flushes stdio buffers before dying. This means
  514.  * standard io library is included whether you wanted it or not - add a
  515.  * dummy "_cleanup() {}" declaration to avoid it if you're writing very
  516.  * small programs like the Minix utilities. This is more likely to
  517.  * be compatible with other UNIX programs than the exit() in the standard
  518.  * release.
  519.  */ 
  520.  
  521. #include "../include/lib.h"
  522.  
  523. int exit(status)
  524. int status;
  525. {
  526.   _cleanup();
  527.   return callm1(MM, EXIT, status, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR);
  528. }
  529.  
  530.  
  531. /* strncmp() - library module, changed to operate correctly when
  532.  * n is zero. (causes problem in kermit interactive command processor)
  533.  */
  534.  
  535. int strncmp(s1, s2, n)
  536. register char *s1, *s2;
  537. int n;
  538. {
  539. /* Compare two strings, but at most n characters. */
  540.  
  541.   while (n-- != 0) {
  542.     if (*s1 != *s2) return(*s1 - *s2);
  543.     if (*s1 == 0) return(0);
  544.     s1++;
  545.     s2++;
  546.   }
  547.   return 0;
  548. }
  549.  
  550.  
  551. /* These functions based on the similar operations in date.c. No support
  552.  * for timezones or daylight saving - localtime() currently just calls 
  553.  * gmtime() and returns the results unmodified.
  554.  */
  555.  
  556. #include <time.h>
  557.  
  558. static int _days_per_month[] =
  559.   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  560.  
  561. static char *_months[] =
  562.   { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  563.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  564.  
  565. static char *_days[] =
  566.   { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  567.  
  568. static long _s_p_min  = 60L;
  569. static long _s_p_hour = 60L * 60L;
  570. static long _s_p_day  = 60L * 60L * 24L;
  571. static long _s_p_year = 60L * 60L * 24L * 365L;
  572.  
  573.  
  574. char *asctime(tmp)
  575. struct tm *tmp;
  576. {
  577.   static char _asctbuf[30];
  578.  
  579.   sprintf(_asctbuf,"%s %s %2d %02d:%02d:%02d %d\n", 
  580.         _days[tmp->tm_wday], _months[tmp->tm_mon], 
  581.         tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, 
  582.         tmp->tm_year + 1900); 
  583.  
  584.   return _asctbuf;
  585. }
  586.  
  587. struct tm *gmtime(ltp)
  588. long *ltp;
  589. {
  590.   static struct tm _tm;
  591.   char  *p;
  592.   long   t = *ltp;
  593.  
  594.   for (p = (char *)&_tm; p < ((char *)&_tm) + sizeof(_tm); p++)
  595.     *p = 0;
  596.  
  597.   /* get day-of-week. (Add 4 because 1.1.70 was a Thursday) */
  598.   _tm.tm_wday = ((t / _s_p_day)+4L) % 7L;
  599.  
  600.   /* reduce t by years and days, leaving seconds this year. */
  601.   while (t >= _s_p_year) {
  602.     if (((_tm.tm_year + 2) % 4) == 0)
  603.         t -= _s_p_day;
  604.     _tm.tm_year += 1;
  605.     t -= _s_p_year;
  606.   }
  607.   _tm.tm_yday = t / _s_p_day;
  608.  
  609.   /* adjust february for leap year and reduce to seconds this month */
  610.   if (((_tm.tm_year + 2) % 4) == 0)
  611.     _days_per_month[1]++;
  612.   while ( t >= (_days_per_month[_tm.tm_mon] * _s_p_day))
  613.     t -= _days_per_month[_tm.tm_mon++] * _s_p_day;
  614.   _days_per_month[1] = 28;
  615.   _tm.tm_year += 70;
  616.  
  617.   /* find day of month */
  618.   _tm.tm_mday = 1;
  619.   while (t >= _s_p_day) {
  620.     t -= _s_p_day;
  621.     _tm.tm_mday++;
  622.   }
  623.  
  624.   /* and time today */
  625.   while (t >= _s_p_hour) {
  626.     t -= _s_p_hour;
  627.     _tm.tm_hour++;
  628.   }
  629.   while (t >= _s_p_min) {
  630.     t -= _s_p_min;
  631.     _tm.tm_min++;
  632.   }
  633.   _tm.tm_sec = (int) t;
  634.  
  635.   return &_tm;
  636. }
  637.  
  638. struct tm *localtime(ltp)
  639. long *ltp;
  640. {
  641.   return gmtime(ltp);
  642. }
  643.  
  644. /* Modify do_printf to accept the K&R specification of %ld, %lx for
  645.  * long integer printf formats. The change is trivial, and the Minix 
  646.  * %D, %X format can still be supported.
  647.  */
  648.  
  649. ( not all the function listed here ............. )
  650.  
  651.  
  652. ~~~~~~~~
  653. line 16
  654. ~~~~~~~~
  655.     int  r,
  656.         w1, w2,
  657.         sign,
  658.         lformat;
  659.     long l;
  660.  
  661.  
  662. ~~~~~~~~~~~~~~~
  663. line 36
  664. ~~~~~~~~~~~~~~~
  665.  
  666.         padchar = ' ';
  667.         lformat = 0;
  668.         format++;
  669.  
  670. ~~~~~~~~~~~~~~~
  671. after line 60
  672. ~~~~~~~~~~~~~~~
  673.  
  674.         if ( *format == 'l'){
  675.             lformat = 0x80;
  676.             format++;
  677.         }
  678.  
  679.         switch (*format + lformat) {
  680.         case 'd':
  681.             l = (long) GET_ARG(vl, int);
  682.  
  683. ~~~~~~~~~~~~~~~~~
  684. line 76
  685. ~~~~~~~~~~~~~~~~~
  686.  
  687.             break;
  688.         case 'x':
  689.             l = (long) GET_ARG(vl, int);
  690.             if (l < 0) l = l & 0xFFFF;
  691.             r = 16;
  692.             break;
  693.         case 'D':
  694.         case 'd' + 0x80:
  695.             l = (long) GET_ARG(vl, long);
  696.             r = 10;
  697.             break;
  698.         case 'O':
  699.         case 'o' + 0x80:
  700.             l = (long) GET_ARG(vl, long);
  701.             r = 8;
  702.             break;
  703.         case 'X':
  704.         case 'x' + 0x80:
  705.             l = (long) GET_ARG(vl, long);
  706.             r = 16;
  707.             break;
  708.         case 'c':
  709.             c = (char) GET_ARG(vl, int); 
  710.             /* char's are casted back to int's */
  711.  
  712. ~~~~~~~~~~~~~~~~~~~~~~
  713. ~~~~~~~~~~~~~~~~~~~~~~~
  714.  
  715.  
  716.  
  717. The following fixes may be regarded as dubious, since they actually make
  718. the operation of minix less like standard unix. However, the performance
  719. of unbuffered tty output (as used in kermit help messages and command 
  720. completion) is so appalling I felt something was needed. 
  721.  
  722. If a call is made to any of the string output routines (printf, puts ..),
  723. when the channel is unbuffered, then a temporary buffer is attached to
  724. the channel for the duration of the call and flushed at the end. Since
  725. setting stdout unbuffered freed it's buffer allocation, the buffer used
  726. for this is probably already available on the heap. 
  727.  
  728. The speed-up effect of this on unbuffered i/o (as used for all stderr
  729. in the system utilities) is startling, and I can't think why these changes 
  730. should cause any incompatibilities with standard Unix usage. 
  731.  
  732.  
  733. #include <stdio.h>
  734.  
  735. /* tmpbuf() creates a temporary buffer for the file, if possible.
  736.  * Only call this if the file is unbuffered.
  737.  */
  738.  
  739. static char *_locbuf = NULL;
  740. static int _locflags;
  741.  
  742. static void tmpbuf(file)
  743. register FILE *file;
  744. {
  745.     if (_locbuf == NULL)        /* no buffer - get one */
  746.     _locbuf = malloc(BUFSIZ);
  747.  
  748.     if (_locbuf == NULL)        /* no space - cannot buffer */
  749.     return;
  750.  
  751.     _locflags = file->_flags;
  752.     file->_flags &= ~UNBUFF;
  753.     file->_buf = file->_ptr = _locbuf;
  754.     file->_count = 0;
  755. }
  756.  
  757. /* endbuf() restores the FILE flags and flushes the buffer if required. */
  758.  
  759. static endbuf(file, fflag)
  760. register FILE *file;
  761. int fflag;
  762. {
  763.     if (fflag || file->_buf == _locbuf)
  764.     fflush(file);
  765.  
  766.     if (file->_buf == _locbuf && _locbuf != NULL) {
  767.  
  768.     /* temporary buffer used - restore old settings */
  769.     file->_buf = file->_ptr = NULL;
  770.     file->_count = 0;
  771.     file->_flags = _locflags;
  772.     }
  773. }
  774.  
  775.  
  776.  
  777. /* puts() - replaces macro in stdio.h, since a newline is required 
  778.  * after the given string. 
  779.  */
  780.  
  781. puts(s)
  782. register char *s;
  783. {
  784.     if ( testflag(stdout, UNBUFF) )
  785.         tmpbuf(stdout);
  786.  
  787.     while ( *s ) 
  788.         putc(*s++,stdout);
  789.     putc('\n',stdout);
  790.  
  791.     endbuf(stdout, 1);    /* always flush */
  792. }
  793.  
  794.  
  795. fputs(s,file)
  796. register char *s;
  797. FILE *file;
  798. {
  799.     if ( testflag(file, UNBUFF) )
  800.         tmpbuf(file);
  801.  
  802.     while ( *s ) 
  803.         putc(*s++,file);
  804.  
  805.     endbuf(file, 0);
  806. }
  807.  
  808.  
  809.  
  810. fprintf (file, fmt, args)
  811. FILE *file;
  812. char *fmt;
  813. int args;
  814. {
  815.     if ( testflag(file, UNBUFF) )
  816.         tmpbuf(file);
  817.  
  818.     _doprintf (file, fmt, &args);
  819.  
  820.     endbuf(file, testflag(file, PERPRINTF));
  821. }
  822.  
  823.  
  824. printf (fmt, args)
  825. char *fmt;
  826. int args;
  827. {
  828.     if ( testflag(stdout, UNBUFF) )
  829.         tmpbuf(stdout);
  830.  
  831.     _doprintf (stdout, fmt, &args);
  832.  
  833.     endbuf(stdout, testflag(stdout, PERPRINTF));
  834. }
  835.  
  836.  
  837.  
  838. Changes to Minix
  839. ================
  840.  
  841.     Here's where it gets more experimental: I've done the following fixes 
  842. to MINIX in order to get Kermit working or to solve problems I've found along 
  843. the way. Some may not be essential - I didn't want to rebuild with all my 
  844. older fixes taken out just to determine which are necessary. Since all the 
  845. fixes are for major problems, they're worth doing anyway.
  846.  
  847.  
  848.     Any SIGALRM sent to a process will cancel all pending alarms. This 
  849. has the effect of suspending forever (say) the update process if another
  850. process executes sleep(). MM/signal.c, check_sig() resets not only the 
  851. alarm bit for process getting the signal, but for all others. Resetting the
  852. bit needs to be conditional on the state of the send_sig flag. The clock
  853. task looks in the message field CLOCK_PROC_NR for the process number of
  854. the task that needs the alarm signal. MM actually puts the number in the 
  855. field PROC_NR, which is not the same. Hence the clock actually sends the 
  856. signal to process 0, which is taken to mean all processes.
  857.  
  858.     It is possible for a message from FS to a task to be overwritten 
  859. before it reaches its destination if the task supports multiple minor
  860. devices and process suspension, as a TTY driver with more than one line 
  861. would. This will occur if the TTY driver has suspended a task, and is busy
  862. with an interrupt request (which will revive the task) when FS send-receives
  863. a message about a different minor device. FS will be forced to wait for the 
  864. task until the REVIVE message appears. FS is then sent the message, since it 
  865. appears to be waiting for a reply. The tty task will then look for more 
  866. messages and will find the FS request, now overwritten with the REVIVE message. 
  867. The TTY will be given its own REVIVE message and reject it as invalid. FS 
  868. will continue to wait for a reply to it's TTY request (now lost) and hang.
  869.  
  870.     I have fixed this by treating REVIVE messages in the same way as 
  871. signals they are rather similar, since they are sent to a caller which is not
  872. waiting for them. Instead of replying with a REVIVE message, I call
  873. set_revive(), a function similar to cause_sig() which sets a flag in the 
  874. proc table p_flags and saves the reply status in a new member, p_status.
  875. Inform() is then called for FS as well as MM, and searches for the revive
  876. flag. Any pending revives are actioned as a HARDWARE message, just like a 
  877. kernel signal. FS will then no longer get a reply from a previous message
  878. when send-receiving to a task, since the REVIVE call comes from HARDWARE.
  879. FS can thus be simplified in areas like rw_dev().
  880.  
  881.     FS may panic with a "couldn't revive anyone" error when it recovers 
  882. a process from waiting on a broken pipe. If the pipe is reading from two file
  883. descriptors (as in the Kermit procedure to pipe process stdout and stderr to 
  884. a remote machine for the REMOTE HOST command), then FS/misc.c, do_exit() will 
  885. call FS/open.c, do_close() for both descriptors. On each call, FS/pipe.c, 
  886. release() will be called, setting the REVIVING flag for the reading process (e.g. the
  887. Kermit) twice, and also incrementing 'reviving'. FS/main.c, get_work() revives
  888. the reading process, then can't find a process to account for the other 
  889. increment of 'reviving'. release() should check for the REVIVING flag before
  890. calling revive() again.
  891.  
  892.     FS can send a cancel message for an out-of-range tty number when a 
  893. process is killed, due to FS/pipe.c, do_unpause() not setting up the global 
  894. fp before calling get_filp(). The tty driver does not catch out-of-range line 
  895. numbers on either messages or characters from the overrun buffer, and can 
  896. index a tty structure that doesn't exist.
  897.  
  898.     Do_unpause() doesn't check that get_filp() returns a valid result.
  899. The return can be NULL if the file (that the process is paused on) has been
  900. closed. This happens if a signal killed the process. The distributed code of 
  901. MM/signal.c, check_sig() differs from the printed listings in Tanenbaum's 
  902. book in the order of execution of unpause() and sig_proc(). This means that 
  903. FS expects do_unpause to have a process to restart, when the filehandles it 
  904. should look at are already closed. It seems OK to return the order of the 
  905. calls to their original state, but I'd like to know why they changed.
  906.  
  907.     CONTROL-S will pause the user process by waiting before replying to 
  908. FS. This causes the entire system to get stuck waiting for FS to return from
  909. the TTY call. The TTY needs to SUSPEND the process on output, just as it does 
  910. on input. Do_cancel() in the TTY driver also has to change to ensure that FS
  911. obtains a reply or a REVIVE when output is completed.
  912.  
  913. The following comments are not bug reports, but code changes I have done
  914. to improve the system's performance at high interrupt rates. I found
  915. character processing at 9600 baud on an 8MHz 8086 a problem, and had to
  916. make several changes :
  917.  
  918.     When interrupts are arriving at high speed (characters at 9600 baud),
  919. some may be lost due to the time taken to process an interrupt in the kernel.
  920. This time may be reduced by moving the check for now-ready tasks in kernel/
  921. proc.c, interrupt() to the end of mini-rec(). This reduces the number of calls
  922. to mini-send within the interrupt handler to 1. If a clock interrupt was 
  923. pending, the clock processing can double or triple the time needed to process
  924. the call to interrupt(). If the tty task is often scheduled, the clock task
  925. is often behind, since tasks do not preempt each other. Thus clock interrupts
  926. often are pending when interrupt() is called at high repetition rates.
  927.  
  928.     However, placing this check in mini_rec() will mean that interrupts 
  929. will always be serviced when the task becomes ready. This, while reducing the 
  930. interrupt latency of a task, means that fast interrupts can make the task 
  931. permanently computable. No other process can then obtain any time to read 
  932. the incoming characters, and the task's buffer fills. Flowcontrol and possibly
  933. ignoring unbufferable input makes it possible to empty the buffer again. 
  934. File transfer with Kermit is OK, because the packets are considerably smaller
  935. than the task's buffer, but packets longer than the buffer, sliding windows 
  936. and connect sessions will have problems if flowcontrol is not possible. 
  937.  
  938.     The mini_send() call in interrupt() should also be removed and the 
  939. message copy be performed directly within interrupt(), avoiding the 
  940. substantial checking performed on call parameters, which should be unnecessary 
  941. on messages within the kernel. The message copy might also be performed by 
  942. a faster version of copy_mess(), for use within the kernel, which need not
  943. span segments. Better still, since interrupts may overwrite each other and 
  944. only affect a task's private data area, they tend to use a single known
  945. buffer for data transfer to the calling task. (I haven't made these last 2 changes yet).
  946.  
  947.     The above changes permitted rather operation in connect mode at 9600
  948. baud on an 8Mhz 8086, using a kernel compiled with the Lattice compiler.
  949. The interrupt routine was taking most of the time   - the application 
  950. program could only run when a break in transmission occurred. Since I use
  951. the system mostly with a modem at 1200 baud, this was acceptable. An AT 
  952. based system could probably do better, but the Minix compiler produces 
  953. poorer code than Lattice, and a Minix-built kernel will be a little slower.
  954.  
  955.  
  956.  
  957. The following code fragments indicate most of the above fixes. Since the code
  958. was originally that distributed by Prentice-Hall, I have included their
  959. copyright notice.
  960.  
  961. /* Copyright (C) 1987 by Prentice-Hall, Inc.  Permission is hereby granted to
  962.  * private individuals and educational institutions to modify and
  963.  * redistribute the binary and source programs of this system to other
  964.  * private individuals and educational institutions for educational and
  965.  * research purposes.  For corporate or commercial use, permission from
  966.  * Prentice-Hall is required.  In general, such permission will be granted,
  967.  * subject to a few conditions.
  968.  */
  969.  
  970. =============
  971. Kernel/proc.c
  972. =============
  973.  
  974. /*===========================================================================*
  975.  *                interrupt                     * 
  976.  *===========================================================================*/
  977. PUBLIC interrupt(task, m_ptr)
  978. int task;            /* number of task to be started */
  979. message *m_ptr;            /* interrupt message to send to the task */
  980. {
  981. /* An interrupt has occurred.  Schedule the task that handles it. */
  982.  
  983.   int this_bit;
  984.   register struct proc *dest_ptr;
  985.  
  986. #ifdef ibmpc
  987.   /* Re-enable the 8259A interrupt controller. */
  988.   this_bit = 1 << (-task);
  989.   port_out(INT_CTL, ENABLE);    /* this re-enables the 8259A controller chip */
  990.   if (pc_at) port_out(INT2_CTL, ENABLE);    /* re-enable second 8259A */
  991. #endif
  992.  
  993.   /* Try to send the interrupt message to the indicated task. */
  994.   this_bit = 1 << (-task);
  995.   dest_ptr = proc_addr(task);
  996.   if ( ((dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING) 
  997.   &&   (dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == HARDWARE) ) {
  998.  
  999.     /* Destination is indeed waiting for this message. */
  1000.     cp_mess(HARDWARE, proc[NR_TASKS+HARDWARE].p_map[D].mem_phys, m_ptr, 
  1001.             dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf);
  1002.     dest_ptr->p_flags &= ~RECEIVING;    /* deblock destination */
  1003.     if (dest_ptr->p_flags == 0) ready(dest_ptr);
  1004.     busy_map &= ~this_bit;    /* turn off the bit in case it was on */
  1005.  
  1006.   } else {
  1007.  
  1008.     /* The message could not be sent to the task; it was not waiting. */
  1009.     if (task == CLOCK) {
  1010.         lost_ticks++;
  1011.     } else {
  1012.         busy_map |= this_bit;        /* mark task as busy */
  1013.         task_mess[-task] = m_ptr;    /* record message pointer */
  1014.     }
  1015.   }
  1016.  
  1017.   /* If a task has just been readied and a user is running, run the task. */
  1018.   if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE))
  1019.     pick_proc();
  1020. }
  1021.  
  1022. /*===========================================================================*
  1023.  *                mini_send                     * 
  1024.  *===========================================================================*/
  1025. PUBLIC int mini_send(caller, dest, m_ptr)
  1026. int caller;            /* who is trying to send a message? */
  1027. int dest;            /* to whom is message being sent? */
  1028. message *m_ptr;            /* pointer to message buffer */
  1029. {
  1030. /* Send a message from 'caller' to 'dest'.  If 'dest' is blocked waiting for
  1031.  * this message, copy the message to it and unblock 'dest'.  If 'dest' is not
  1032.  * waiting at all, or is waiting for another source, queue 'caller'.
  1033.  */
  1034.  
  1035.   register struct proc *caller_ptr, *dest_ptr, *next_ptr;
  1036.   vir_bytes vb;            /* message buffer pointer as vir_bytes */
  1037.   vir_clicks vlo, vhi;        /* virtual clicks containing message to send */
  1038.   vir_clicks len;        /* length of data segment in clicks */
  1039.  
  1040.   /* User processes are only allowed to send to FS and MM.  Check for this. */
  1041.   if (caller >= LOW_USER && (dest != FS_PROC_NR && dest != MM_PROC_NR))
  1042. #ifdef DEBUG
  1043.     if (dest != SYSTASK) return(E_BAD_DEST);
  1044. #else
  1045.     return(E_BAD_DEST);
  1046. #endif
  1047.   caller_ptr = proc_addr(caller);    /* pointer to source's proc entry */
  1048.   dest_ptr = proc_addr(dest);    /* pointer to destination's proc entry */
  1049.   if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST);    /* dead dest */
  1050.  
  1051.   /* Check for messages wrapping around top of memory or outside data seg. */
  1052.   len = caller_ptr->p_map[D].mem_len;
  1053.   vb = (vir_bytes) m_ptr;
  1054.   vlo = vb >> CLICK_SHIFT;    /* vir click for bottom of message */
  1055.   vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT;    /* vir click for top of message */
  1056.   if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)return(E_BAD_ADDR);
  1057.  
  1058.   /* Check to see if 'dest' is blocked waiting for this message. */
  1059.  
  1060.   if ( ((dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING) &&
  1061.         (dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) {
  1062.  
  1063.     /* Destination is indeed waiting for this message. */
  1064.     cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr, 
  1065.                 dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf);
  1066.     dest_ptr->p_flags &= ~RECEIVING;    /* deblock destination */
  1067.     if (dest_ptr->p_flags == 0) ready(dest_ptr);
  1068.  
  1069.   } else {
  1070.     /* Destination is not waiting.  Block and queue caller. */
  1071.     if (caller == HARDWARE) return(E_OVERRUN);
  1072.     caller_ptr->p_messbuf = m_ptr;
  1073.     caller_ptr->p_flags |= SENDING;
  1074.     unready(caller_ptr);
  1075.  
  1076.     /* Process is now blocked.  Put in on the destination's queue. */
  1077.     if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) {
  1078.         dest_ptr->p_callerq = caller_ptr;
  1079.     } else {
  1080.         while (next_ptr->p_sendlink != NIL_PROC)
  1081.             next_ptr = next_ptr->p_sendlink;
  1082.         next_ptr->p_sendlink = caller_ptr;
  1083.     }
  1084.     caller_ptr->p_sendlink = NIL_PROC;
  1085.   }
  1086.  
  1087. #ifdef DEBUG
  1088. /* This message has been successfully handled. See if it should be saved
  1089.  * in the message log. 
  1090.  */
  1091.   if ((save_flags[caller+NR_TASKS] & CALL_LOG) 
  1092.   ||  (save_flags[dest  +NR_TASKS] & DEST_LOG))
  1093.     save_msg(caller, dest, m_ptr);
  1094. #endif
  1095.  
  1096.   return(OK);
  1097. }
  1098.  
  1099.  
  1100. /*===========================================================================*
  1101.  *                mini_rec                     * 
  1102.  *===========================================================================*/
  1103. PRIVATE int mini_rec(caller, src, m_ptr)
  1104. int caller;            /* process trying to get message */
  1105. int src;            /* which message source is wanted (or ANY) */
  1106. message *m_ptr;            /* pointer to message buffer */
  1107. {
  1108. /* A process or task wants to get a message.  If one is already queued,
  1109.  * acquire it and deblock the sender.  If no message from the desired source
  1110.  * is available, block the caller.  No need to check parameters for validity.
  1111.  * Users calls are always sendrec(), and mini_send() has checked already.  
  1112.  * Calls from the tasks, MM, and FS are trusted.
  1113.  */
  1114.  
  1115.   register struct proc *caller_ptr, *sender_ptr, *prev_ptr;
  1116.   int sender, this_bit;
  1117.   int locked = FALSE;
  1118.  
  1119.   caller_ptr = proc_addr(caller);    /* pointer to caller's proc structure */
  1120.  
  1121.   /* if we're ready to receive, find a sender. Else just block. */
  1122.   if ((caller_ptr->p_flags & SENDING) == 0) {
  1123.  
  1124.     /* Check to see if a message from desired source is already available. */
  1125.  
  1126.     sender_ptr = caller_ptr->p_callerq;
  1127.     while (sender_ptr != NIL_PROC) {
  1128.     sender = sender_ptr - proc - NR_TASKS;
  1129.     if (src == ANY || src == sender) {
  1130.         /* An acceptable message has been found. */
  1131.         cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf,
  1132.                     caller_ptr->p_map[D].mem_phys, m_ptr);
  1133.         sender_ptr->p_flags &= ~SENDING;    /* deblock sender */
  1134.         if (sender_ptr->p_flags == 0) ready(sender_ptr);
  1135.         if (sender_ptr == caller_ptr->p_callerq)
  1136.             caller_ptr->p_callerq = sender_ptr->p_sendlink;
  1137.         else
  1138.             prev_ptr->p_sendlink = sender_ptr->p_sendlink;
  1139.         return(OK);
  1140.     }
  1141.     prev_ptr = sender_ptr;
  1142.     sender_ptr = sender_ptr->p_sendlink;
  1143.     }
  1144.  
  1145.     /* If the caller is a task, there may be an interrupt waiting. 
  1146.      * Check now, rather than before checking callers, so fast interrupts
  1147.      * give the process a chance to absorb them. Lock between determining the
  1148.      * state of busy_map and setting RECEIVING so an interrupt occurring 
  1149.      * between cannot be forgotten.
  1150.      */
  1151.  
  1152.     if (caller < HARDWARE && (src == ANY || src == HARDWARE)) {
  1153.  
  1154.     lock();
  1155.     locked = TRUE;
  1156.     this_bit = 1 << (-caller);
  1157.     if (busy_map & this_bit) {
  1158.         cp_mess(HARDWARE, proc[NR_TASKS+HARDWARE].p_map[D].mem_phys,
  1159.             task_mess[-caller], caller_ptr->p_map[D].mem_phys,
  1160.             m_ptr);
  1161.         busy_map &= ~this_bit;        /* must be locked here too */
  1162.         restore();
  1163.         return(OK);
  1164.     }
  1165.     }
  1166.   }
  1167.  
  1168.   /* No suitable message is available. Block the process trying to receive. */
  1169.  
  1170.   caller_ptr->p_getfrom = src;
  1171.   caller_ptr->p_messbuf = m_ptr;
  1172.   caller_ptr->p_flags |= RECEIVING;
  1173.   if (locked) restore();
  1174.   unready(caller_ptr);
  1175.  
  1176.   /* If MM has just blocked and there are kernel signals pending, now is the
  1177.    * time to tell MM about them, since it will be able to accept the message.
  1178.    * Also applies to revive messages for FS. No problems with receive-before-
  1179.    * send, since (src == ANY) cannot be true for sendrec messages.
  1180.    */
  1181.  
  1182.   if (rev_procs > 0 && caller == FS_PROC_NR && src == ANY) inform(FS_PROC_NR);
  1183.   if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR);
  1184.   return(OK);
  1185. }
  1186.  
  1187. ===============
  1188. kernel\system.c
  1189. ===============
  1190.  
  1191. /*===========================================================================*
  1192.  *                set_revive                     * 
  1193.  *===========================================================================*/
  1194. PUBLIC set_revive(proc_nr, status)
  1195. int proc_nr;
  1196. int status;
  1197. /* Similar function to cause_sig. Tasks sending a revive message directly 
  1198.  * to FS may collide with a send in the opposite direction. Therefore, this
  1199.  * function saves the event and associated status (error code, bytes read etc)
  1200.  * in the target process' proc entry, and the kernel informs FS later with
  1201.  * inform(FS_PROC_NR). It would be nice to share the proc table's p_pending
  1202.  * member with MM, but the signals recorded in it may be ignored - only MM
  1203.  * knows. Note that the device's id is lost, and only FS may receive a message.
  1204.  * This is not currently a problem.
  1205.  */
  1206. {
  1207.   register struct proc *rp;
  1208.  
  1209.   rp = proc_addr(proc_nr);
  1210.   if (rp->p_flags & NEED_REVIVE) 
  1211.     panic ("cannot revive twice",proc_nr);
  1212.   rp->p_flags |= NEED_REVIVE;
  1213.   rp->p_rstatus = status;
  1214.   rev_procs++;
  1215.  
  1216.   inform(FS_PROC_NR);        /* perhaps it's safe to do it now ? */
  1217. }
  1218.  
  1219. /*===========================================================================*
  1220.  *                inform                         * 
  1221.  *===========================================================================*/
  1222. PUBLIC inform(proc_nr)
  1223. int proc_nr;            /* MM_PROC_NR or FS_PROC_NR */
  1224. {
  1225. /* When a signal is detected by the kernel (e.g., DEL), or generated by a task
  1226.  * (e.g. clock task for SIGALRM), cause_sig() is called to set a bit in the
  1227.  * p_pending field of the process to signal.  Then inform() is called to see
  1228.  * if MM is idle and can be told about it.  Whenever MM blocks, a check is
  1229.  * made to see if 'sig_procs' is nonzero; if so, inform() is called.
  1230.  * Likewise REVIVE messages for FS. These are sent only when FS is ready to
  1231.  * receive. Code here changed to check recipient is not blocked on send as
  1232.  * well as receive.
  1233.  */
  1234.  
  1235.   register struct proc *rp, *mmp;
  1236.   int r;
  1237.  
  1238.   /* If MM/FS is not waiting for new input, forget it. */
  1239.   mmp = proc_addr(proc_nr);
  1240.   if ( ((mmp->p_flags & RECEIVING) == 0) || mmp->p_getfrom != ANY) return;
  1241.  
  1242.   /* If MM is waiting for new input,  find a process with pending signals. */
  1243.   if (proc_nr == MM_PROC_NR) {
  1244.     for (rp = proc_addr(0); rp < proc_addr(NR_PROCS); rp++)
  1245.     if (rp->p_pending != 0) {
  1246.         m.m_type = KSIG;
  1247.         m.PROC1 = rp - proc - NR_TASKS;
  1248.         m.SIG_MAP = rp->p_pending;
  1249.         sig_procs--;
  1250.         if ((r=mini_send(HARDWARE, proc_nr, &m)) != OK) 
  1251.             panic("can't inform MM : ", r);
  1252.         rp->p_pending = 0;    /* the ball is now in MM's court */
  1253.         return;
  1254.     }
  1255.   }
  1256.  
  1257.   /* If FS is waiting for new input,  find a process with pending revive. */
  1258.   else if (proc_nr == FS_PROC_NR) {
  1259.     for (rp = proc_addr(0); rp < proc_addr(NR_PROCS); rp++)
  1260.     if (rp->p_flags & NEED_REVIVE) {
  1261.         m.m_type = REVIVE;
  1262.         m.REP_PROC_NR = rp - proc - NR_TASKS;
  1263.         m.REP_STATUS  = rp->p_rstatus;
  1264.         rev_procs--;
  1265.         rp->p_flags &= ~NEED_REVIVE;
  1266.         if ((r=mini_send(HARDWARE, proc_nr, &m)) != OK) 
  1267.             panic("can't inform FS : ", r);
  1268.         return;        /* the ball is now in FS's court */
  1269.     }
  1270.   }
  1271. }
  1272.  
  1273. ===========
  1274. mm/signal.c
  1275. ===========
  1276.  
  1277. /*===========================================================================*
  1278.  *                check_sig                     *
  1279.  *===========================================================================*/
  1280. PRIVATE int check_sig(proc_id, sig_nr, send_uid)
  1281. int proc_id;            /* pid of process to signal, or 0 or -1 */
  1282. int sig_nr;            /* which signal to send (1-16) */
  1283. uid send_uid;            /* identity of process sending the signal */
  1284. {
  1285. /* Check to see if it is possible to send a signal.  The signal may have to be
  1286.  * sent to a group of processes.  This routine is invoked by the KILL system
  1287.  * call, and also when the kernel catches a DEL or other signal. SIGALRM too.
  1288.  */
  1289.  
  1290.   register struct mproc *rmp;
  1291.   int count, send_sig;
  1292.   unshort mask;
  1293.   extern unshort core_bits;
  1294.  
  1295.   if (sig_nr < 1 || sig_nr > NR_SIGS) return(EINVAL);
  1296.   count = 0;            /* count # of signals sent */
  1297.   mask = 1 << (sig_nr - 1);
  1298.  
  1299.   /* Search the proc table for processes to signal.  Several tests are made:
  1300.    *     - if proc's uid != sender's, and sender is not superuser, don't signal
  1301.    *    - if specific process requested (i.e., 'procpid' > 0, check for match
  1302.    *    - if a process has already exited, it can't receive signals
  1303.    *    - if 'proc_id' is 0 signal everyone in same process group except caller
  1304.    */
  1305.   for (rmp = &mproc[INIT_PROC_NR + 1]; rmp < &mproc[NR_PROCS]; rmp++ ) {
  1306.     if ( (rmp->mp_flags & IN_USE) == 0) continue;
  1307.     send_sig = TRUE;    /* if it's FALSE at end of loop, don't signal */
  1308.     if (send_uid != rmp->mp_effuid && send_uid != SUPER_USER)send_sig=FALSE;
  1309.     if (proc_id > 0 && proc_id != rmp->mp_pid) send_sig = FALSE;
  1310.     if (rmp->mp_flags & HANGING) send_sig = FALSE;   /*don't wake the dead*/
  1311.     if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) send_sig = FALSE;
  1312.     if (send_uid == SUPER_USER && proc_id == -1) send_sig = TRUE;
  1313.  
  1314.     /* SIGALARM is a little special.  When a process exits, a clock signal
  1315.      * can arrive just as the timer is being turned off.  Also, turn off
  1316.      * ALARM_ON bit when timer goes off to keep it accurate.
  1317.      */
  1318.     /* Change : only reset the ALARM_ON bit for processes that are
  1319.      * going to get the signal - don't wipe out all the other alarms! 
  1320.      */
  1321.  
  1322.     if (send_sig && sig_nr == SIGALRM) {
  1323.         if ( (rmp->mp_flags & ALARM_ON) == 0) continue;
  1324.         rmp->mp_flags &= ~ALARM_ON;
  1325.     }
  1326.  
  1327.     if (send_sig == FALSE || rmp->mp_ignore & mask) continue;
  1328.  
  1329.     /* If process is hanging on PAUSE, WAIT, tty, pipe, etc. release it. */
  1330.     unpause(rmp - mproc);    /* check to see if process is paused */
  1331.     count++;
  1332.  
  1333.     /* Send the signal or kill the process, possibly with core dump. */
  1334.     sig_proc(rmp, sig_nr);
  1335.  
  1336.     if (proc_id > 0) break;    /* only one process being signalled */
  1337.   }
  1338.  
  1339.   /* If the calling process has killed itself, don't reply. */
  1340.   if ((mp->mp_flags & IN_USE) == 0 || (mp->mp_flags & HANGING))dont_reply =TRUE;
  1341.   return(count > 0 ? OK : ESRCH);
  1342. }
  1343.  
  1344.  
  1345. /*===========================================================================*
  1346.  *                set_alarm                     *
  1347.  *===========================================================================*/
  1348. PUBLIC int set_alarm(proc_nr, sec)
  1349. int proc_nr;            /* process that wants the alarm */
  1350. unsigned sec;            /* how many seconds delay before the signal */
  1351. {
  1352. /* This routine is used by do_alarm() to set the alarm timer.  It is also
  1353.  * to turn the timer off when a process exits with the timer still on.
  1354.  */
  1355.  
  1356.   int remaining;
  1357.  
  1358.   m_sig.m_type = SET_ALARM;
  1359.  
  1360. /*---  m_sig.PROC_NR = proc_nr;    ---*/
  1361.   m_sig.CLOCK_PROC_NR = proc_nr;    /* clock uses different member     */
  1362.  
  1363.   m_sig.DELTA_TICKS = HZ * sec;
  1364.   if (sec != 0)
  1365.     mproc[proc_nr].mp_flags |= ALARM_ON;    /* turn ALARM_ON bit on */
  1366.   else
  1367.     mproc[proc_nr].mp_flags &= ~ALARM_ON;    /* turn ALARM_ON bit off */
  1368.  
  1369.   /* Tell the clock task to provide a signal message when the time comes. */
  1370.   if (sendrec(CLOCK, &m_sig) != OK) panic("alarm er", NO_NUM);
  1371.   remaining = (int) m_sig.SECONDS_LEFT;
  1372.   return(remaining);
  1373. }
  1374.  
  1375. =========
  1376. fs/pipe.c
  1377. =========
  1378.  
  1379.  
  1380. /*===========================================================================*
  1381.  *                release                         *
  1382.  *===========================================================================*/
  1383. PUBLIC release(ip, call_nr, count)
  1384. register struct inode *ip;    /* inode of pipe */
  1385. int call_nr;            /* READ or WRITE */
  1386. int count;            /* max number of processes to release */
  1387. {
  1388. /* Check to see if any process is hanging on the pipe whose inode is in 'ip'.
  1389.  * If one is, and it was trying to perform the call indicated by 'call_nr'
  1390.  * (READ or WRITE), release it.
  1391.  */
  1392.  
  1393.   register struct fproc *rp;
  1394.  
  1395.   /* Search the proc table. */
  1396.   for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) {
  1397.     if (rp->fp_suspended == SUSPENDED 
  1398.     && (rp->fp_fd & BYTE) == call_nr 
  1399.     &&  rp->fp_revived != REVIVING
  1400.     &&  rp->fp_filp[rp->fp_fd>>8]->filp_ino == ip) {
  1401.         revive(rp - fproc, 0);
  1402.         susp_count--;    /* keep track of who is suspended */
  1403.         if (--count == 0) return;
  1404.     }
  1405.   }
  1406. }
  1407.  
  1408.  
  1409. /*===========================================================================*
  1410.  *                do_unpause                     *
  1411.  *===========================================================================*/
  1412. PUBLIC int do_unpause()
  1413. {
  1414. /* A signal has been sent to a user who is paused on the file system.
  1415.  * Abort the system call with the EINTR error message.
  1416.  */
  1417.  
  1418.   register struct fproc *rfp;
  1419.   int proc_nr, task, susfd;
  1420.   struct filp *f;
  1421.   dev_nr dev;
  1422.   extern struct filp *get_filp();
  1423.  
  1424.   if (who > MM_PROC_NR) return(EPERM);
  1425.   proc_nr = pro;
  1426.   if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("unpause err 1", proc_nr);
  1427.   rfp = &fproc[proc_nr];
  1428.   if (rfp->fp_suspended == NOT_SUSPENDED) return(OK);
  1429.   task = -rfp->fp_task;
  1430.  
  1431.   if (task != XPIPE) {
  1432.     susfd = rfp->fp_fd >> 8;
  1433.     f = rfp->fp_filp[susfd];    /* don't use get_filp - fp is wrong */
  1434.     if (susfd > NR_FDS || susfd < 0 || f == NIL_FILP) {
  1435.         panic("unpause err 4", proc_nr);
  1436.     }
  1437.     dev = f->filp_ino->i_zone[0];    /* device on which proc is hanging */
  1438.     mess.TTY_LINE = (dev >> MINOR) & BYTE;
  1439.     mess.PROC_NR = proc_nr;
  1440.     mess.m_type = CANCEL;
  1441.     if (sendrec(task, &mess) != OK) panic("unpause err 2", NO_NUM);
  1442.     while (mess.REP_PROC_NR != proc_nr) {
  1443.         revive(mess.REP_PROC_NR, mess.REP_STATUS);
  1444.         if (receive(task, &m) != OK) panic("unpause err 3", NO_NUM);
  1445.     }
  1446.     revive(proc_nr, EINTR);    /* signal interrupted call */
  1447.   }
  1448.  
  1449.   return(OK);
  1450. }
  1451.  
  1452.  
  1453.  
  1454.     I haven't included my asynchronous tty driver, as I don't use IBM 
  1455. hardware and anybody interested in Kermit on Minix probably has their own
  1456. ideas. However, an outline of the driver follows for anybody who might be
  1457. interested (I'm happy to answer any further queries or comments as I 
  1458. continue this development).
  1459.  
  1460.     The tty driver is split into three parts:
  1461.  
  1462. tty.c        Contains the 'device independent' part of the original driver.
  1463. screen.c    Contains hardware-specific code for console screen and
  1464.         keyboard driver.
  1465. async.c        Contains hardware-specific code for asynchronous serial
  1466.         tty driver.
  1467.  
  1468.  
  1469. The tty_struct structure is extended, and now contains fields for ioctl
  1470. function addresses (so do_ioctl sets characters, modes etc then calls
  1471. the device - dependent code for baud rate, etc) and a single character
  1472. echo function for that port. Tty_ramqueue is a union of the integer queue 
  1473. required for screen driving and a char queue for buffering output, ready 
  1474. for async interrupts. The flag used for RUNNING / STOPPED indication is 
  1475. now a bit mask, another bit being STALLED, to indicate that an XOFF has 
  1476. been sent to the device on the other end of the serial line. 
  1477.  
  1478. TANDEM flowcontrol is implemented in tty.c, flipping the STALLED bit
  1479. and echoing an XOFF or XON when the buffer contents cross high and
  1480. low water marks. Interrupt processing is sufficiently fast that XOFF
  1481. does not need to be sent if the overrun queue is getting full.
  1482.  
  1483. Transmitter ready interrupts cause a character to be read from tty_rwords
  1484. and written to the UART. When tty_rwords is empty, a message is sent to
  1485. the tty task, and the queue is refilled. In the meantime, the transmitter
  1486. is disabled to avoid continuous interrupts.
  1487.  
  1488. Characters and refill requests are both sent through the same interrupt
  1489. message - use of the suggested TTY_O_DONE message would cause other
  1490. interrupts to be overwritten and lost. The message is now a structure 
  1491. consisting of a count, a queue of ints for character and line number,
  1492. and a bit map for refill requests. Thus refill requests are not lost
  1493. if the input queue is full. The kernel interrupt() call is only
  1494. made if the count of queued characters incremented from zero, or a bit
  1495. was set for the first time in the bitmap. This reduces the interrupt
  1496. processing time if an interrupt had already been sent, but had not been
  1497. processed.
  1498.  
  1499. A bug exists in the processing of input characters - DEL and QUIT send
  1500. a signal to the proper process only if it is the only login. The calculation
  1501. of the proc to be signalled as 'LOW_USER + line + 1' is wrong, since the 
  1502. process slot above the first user is occupied not by the second user's first
  1503. shell but by the update process. For proper processing of tty signals in 
  1504. a multiple-user (or multiple screen) system, FS should tell the tty task when 
  1505. a tty is controlling a process family. Note that this can also be a problem
  1506. in Kermit, since when the external line is not in use, a DEL arriving
  1507. can be sent to all active processes in the system. I have solved this by 
  1508. making use of open() calls to the tty driver to inform the driver of the 
  1509. process slot number when a controlling tty (one opened by the INIT process)
  1510. is opened or closed by FS.
  1511.  
  1512.  
  1513.